package com.ilioili.proabslist;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.Scroller;

public class ProAbsListView extends ViewGroup {

    float firstFingerY;
    float firstFingerX;
    float lastFingerY;
    private AbsListView absListView;
    private int animationDuration = 600;
    private boolean blockTouchEvent;
    private FreshState freshState = FreshState.FRESH_READY;
    private AbsFreshView freshViews;
    private boolean isFreshable = true;
    private boolean isLoadable = true;
    private boolean isPullingDown;
    private boolean isPullingUp;
    private int lastScrollY;
    private LoadState loadState = LoadState.LOAD_READY;
    private AbsLoadView loadViews;
    private int mTouchSlop = 10;
    private boolean passTouchEvnent;
    private Scroller scroller;
    private ListStateListener stateListener;
    private int lastFreshed;

    public ProAbsListView(Context context) {
        this(context, null);
    }

    public ProAbsListView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    @SuppressLint("NewApi")
    public ProAbsListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        scroller = new Scroller(context, new DecelerateInterpolator());
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    }

    public void cancelFreshing() {
        if (freshState == FreshState.FRESHING) {
            freshState = FreshState.FRESH_CANCELING;
            blockTouchEvent = true;
            freshViews.onFreshCompleted();
            scroller.startScroll(0, getScrollY(), 0, -getScrollY(), animationDuration);
            postInvalidate();
        }
    }

    public void cancelLoading() {
        if (loadState == LoadState.LOADING) {
            loadState = LoadState.LOAD_CANCELING;
            blockTouchEvent = true;
            loadViews.onLoadCompleted();
            scroller.startScroll(0, getScrollY(), 0, -getScrollY(), animationDuration * loadViews.getLoadView().getMeasuredHeight() / freshViews.getFreshView().getMeasuredHeight());
            postInvalidate();
        }

    }

    public AbsListView getAbsListView() {
        return absListView;
    }

    @SuppressLint("NewApi")
    public void setAbsListView(AbsListView listView) {
        absListView = listView;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
            absListView.setOverScrollMode(OVER_SCROLL_NEVER);
        }
        addView(absListView);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (blockTouchEvent) {
            return true;
        }
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                isPullingDown = isPullingUp = false;
                lastFingerY = firstFingerY = ev.getY();
                firstFingerX = ev.getX();
                passTouchEvnent = false;
                break;
            case MotionEvent.ACTION_MOVE:
                if (passTouchEvnent) {
                    return false;
                }
                boolean isValideMove = Math.abs(ev.getY() - firstFingerY) + Math.abs(ev.getX() - firstFingerX) > mTouchSlop;
                boolean isHorizontalSlide = Math.abs(ev.getY() - firstFingerY) < Math.abs(ev.getX() - firstFingerX);
                if (isValideMove && isHorizontalSlide) {
                    passTouchEvnent = true;
                    return false;
                }
                if (isValideMove && !isHorizontalSlide) {
                    if (ev.getY() > lastFingerY + 1) {// 下拉
                        lastFingerY = ev.getY();
                        if (isFreshable && ListViewStateUtil.isToTop(absListView) && !isHorizontalSlide) {// AbsListView已经滑动到顶部
                            freshState = FreshState.FRESH_VIEW_VISIBLE;
                            return true;
                        } else {
                            return false;// 上AbsListView来处理
                        }
                    } else if (ev.getY() < lastFingerY - 1) {// 上拉
                        lastFingerY = ev.getY();
                        if (isLoadable && ListViewStateUtil.isToBottom(absListView) && !isHorizontalSlide) {
                            loadState = LoadState.LOAD_VIEW_VISIBLE;
                            return true;
                        } else {
                            return false;
                        }
                    }
                }

        }
        return false;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (!isInEditMode()) {
            if (null != freshViews) {
                freshViews.getFreshView().layout(0, -freshViews.getFreshView().getMeasuredHeight(), getMeasuredWidth(), 0);
            }
            if (null != loadViews) {
                loadViews.getLoadView().layout(0, getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight() + loadViews.getLoadView().getMeasuredHeight());
            }
            absListView.layout(getPaddingLeft(), getPaddingTop(), getMeasuredWidth() - getPaddingRight(), getMeasuredHeight() - getPaddingBottom());
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        if (blockTouchEvent) {
            return false;
        }
        switch (e.getAction()) {
            case MotionEvent.ACTION_DOWN:

                break;
            case MotionEvent.ACTION_MOVE:

                if (Math.abs(firstFingerY - e.getY() - lastScrollY) > 1) {
                    // if(Math.abs(getScrollY()-lastScrollY)>1){
                    int currentScrollY = (int) (firstFingerY - e.getY());
                    lastScrollY = getScrollY();
                    int percentage = 0;
                    if (currentScrollY < 0) {
                        if (isPullingUp == true) {
                            return false;
                        }
                        isPullingDown = true;
                        percentage = -currentScrollY * 100 / freshViews.getFreshView().getMeasuredHeight();
                        freshViews.onScrollWhileFreshViewVisible(percentage);
                        stateListener.onScrollWhileFreshViewVisible(percentage);
                    } else if (currentScrollY > 0) {
                        if (isPullingDown) {
                            return false;
                        }
                        isPullingUp = true;
                        percentage = currentScrollY * 100 / freshViews.getFreshView().getMeasuredHeight();
                        loadViews.onScrollWhileLoadViewVisible(percentage);
                        stateListener.onScrollWhileLoadViewVisible(percentage);
                    }
                    scrollTo(0, currentScrollY);
                }
                break;
            case MotionEvent.ACTION_UP:
                if (getScrollY() < freshViews.getFreshView().getTop()) {
                    freshState = FreshState.FRESHING;
                    int duration = (freshViews.getFreshView().getTop() - getScrollY()) * animationDuration / freshViews.getFreshView().getMeasuredHeight();
                    duration = Math.min(duration, animationDuration);
                    scroller.startScroll(0, getScrollY(), 0, freshViews.getFreshView().getTop() - getScrollY(), duration);
                    blockTouchEvent = true;
                    freshViews.onFreshing();
                    stateListener.onStartFreshing();
                    postInvalidate();

                } else if (getScrollY() <= 0) {
                    freshState = FreshState.FRESH_CANCELING;
                    int duration = -getScrollY() * animationDuration / freshViews.getFreshView().getMeasuredHeight();
                    duration = Math.min(duration, animationDuration);
                    scroller.startScroll(0, getScrollY(), 0, -getScrollY());
                    blockTouchEvent = true;
                    postInvalidate();

                } else if (getScrollY() + getMeasuredHeight() <= loadViews.getLoadView().getBottom()) {
                    loadState = LoadState.LOAD_CANCELING;
                    int duration = getScrollY() * animationDuration / loadViews.getLoadView().getMeasuredHeight();
                    duration = Math.min(duration, animationDuration);
                    scroller.startScroll(0, getScrollY(), 0, -getScrollY());
                    blockTouchEvent = true;
                    postInvalidate();
                } else if (getScrollY() + getMeasuredHeight() > loadViews.getLoadView().getBottom()) {
                    loadState = LoadState.LOADING;
                    int duration = (getScrollY() + getMeasuredHeight() - loadViews.getLoadView().getBottom()) * animationDuration / loadViews.getLoadView().getMeasuredHeight();
                    duration = Math.min(duration, animationDuration);
                    scroller.startScroll(0, getScrollY(), 0, loadViews.getLoadView().getBottom() - getScrollY() - getMeasuredHeight(), duration);
                    blockTouchEvent = true;
                    loadViews.onLoading();
                    stateListener.onStartLoading();
                    postInvalidate();
                }
                break;
        }
        return true;
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (scroller.computeScrollOffset()) {
            scrollTo(0, scroller.getCurrY());
            if (0 == getScrollY()) {
                freshState = FreshState.FRESH_READY;
                loadState = LoadState.LOAD_READY;
                scroller.forceFinished(true);
                blockTouchEvent = false;
            } else if (freshViews.getFreshView().getTop() == getScrollY()) {
                // blockTouchEvent = false;
            } else if (loadViews.getLoadView().getBottom() == getScrollY() + getMeasuredHeight()) {
                // blockTouchEvent = false;
            }
            postInvalidate();
        }
    }

    @TargetApi(Build.VERSION_CODES.GINGERBREAD)
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        absListView = (AbsListView) getChildAt(0);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
            if (null != absListView) {
                absListView.setOverScrollMode(OVER_SCROLL_NEVER);
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (isInEditMode()) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        } else {
            int width = MeasureSpec.getSize(widthMeasureSpec);
            int height = MeasureSpec.getSize(heightMeasureSpec);
            setMeasuredDimension(width, height);
            if (null != freshViews) {
                measureFreshViewOrFootView(freshViews.getFreshView(), widthMeasureSpec);
            }
            if (null != loadViews) {
                measureFreshViewOrFootView(loadViews.getLoadView(), widthMeasureSpec);
            }
            width -= getPaddingLeft() + getPaddingRight();
            height -= getPaddingTop() + getPaddingBottom();
            absListView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
        }
    }

    private void measureFreshViewOrFootView(View v, int widthMeasureSpec) {
        LayoutParams layoutParams = v.getLayoutParams();
        int heightMeasureSpec = 0;
        if (null != layoutParams && layoutParams.height > 0) {
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
        } else {
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        }
        v.measure(widthMeasureSpec, heightMeasureSpec);
    }

    public void setFreshView(AbsFreshView freshViews) {
        this.freshViews = freshViews;
        addView(freshViews.getFreshView());
    }

    public void setFreshable(boolean b) {
        isFreshable = b;
    }

    public void setListStateListener(ListStateListener listener) {
        stateListener = listener;
    }

    public void setLoadView(AbsLoadView loadViews) {
        this.loadViews = loadViews;
        addView(loadViews.getLoadView());
    }

    public void setLoadable(boolean loadable) {
        isLoadable = loadable;
    }

    public void startFresh() {
        if (freshState == FreshState.FRESH_READY) {
            //			absListView.scrollToPosition(0);
            freshViews.onFreshing();
            scroller.startScroll(0, getScrollY(), 0, freshViews.getFreshView().getTop() - getScrollY(), animationDuration);
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    freshState = FreshState.FRESHING;
                    stateListener.onStartFreshing();
                }
            }, animationDuration);
            blockTouchEvent = true;
            postInvalidate();
        }
    }

    public void startLoad() {
        if (loadState == LoadState.LOAD_READY) {
            loadState = LoadState.LOADING;
            absListView.smoothScrollToPosition(absListView.getAdapter().getCount() - 2);
            loadViews.onLoading();
            scroller.startScroll(0, getScrollY(), 0, loadViews.getLoadView().getBottom() - getScrollY() - getMeasuredHeight(), animationDuration);
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    loadViews.onLoading();
                    stateListener.onStartLoading();
                }
            }, animationDuration);
            blockTouchEvent = true;
            postInvalidate();
        }
    }

    public int getLastFreshed() {
        return lastFreshed;
    }

    public void setLastFreshed(int lastFreshed) {
        this.lastFreshed = lastFreshed;
    }

    enum FreshState {
        /**
         * 正在刷新
         */
        FRESHING,
        /**
         * 刷新完成，正在弹回的过程中
         */
        FRESH_CANCELING,
        /**
         * 下拉刷新被调用
         */
        FRESH_INVOKE,
        /**
         * 正常状态，可以下拉刷新
         */
        FRESH_READY,
        /**
         * 刷新View漏出来了
         */
        FRESH_VIEW_VISIBLE;
    }

    enum LoadState {
        /**
         * 正在加载
         */
        LOADING,
        /**
         * 取消加载动画中
         */
        LOAD_CANCELING,
        /**
         * 正常状态，可以下拉加载
         */
        LOAD_READY,
        /**
         * loadView漏出来 了
         */
        LOAD_VIEW_VISIBLE;
    }

}
